﻿/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
#region Using directives

using System;
using System.Collections;
using System.Text;
using System.Reflection;
using ELE.Common.Util;
using ELE.EnterpriseLibrary.Naming.Spi;
using ELE.EnterpriseLibrary.Naming.Configuration;
using ELE.EnterpriseLibrary.Common.Logging;

#endregion

namespace ELE.EnterpriseLibrary.Naming.Factories
{
  /// <summary>
  /// Utility methods that parse objects from context
  /// </summary>
	public class ContextValueParser
	{
    /// <summary>
    /// the string "${"
    /// </summary>
    public const string ESCAPE_LINK_START = GenericConfiguration.ESCAPE_LINK_START;
    /// <summary>
    /// the string "}"
    /// </summary>
    public const string ESCAPE_LINK_END = GenericConfiguration.ESCAPE_LINK_END;
    /// <summary>
    /// the string "root"
    /// </summary>
    public const string ESCAPE_VALUE_ROOT = "root";
    /// <summary>
    /// the string "logger"
    /// </summary>
    public const string ESCAPE_VALUE_LOGGER = "logger";
    /// <summary>
    /// the string "context"
    /// </summary>
    public const string ESCAPE_VALUE_CONTEXT = "context";
    /// <summary>
    /// Tells whether the given string contains the string ${null}
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool HasNullReference(string value)
    {
        string nullRef = ESCAPE_LINK_START + "null" + ESCAPE_LINK_END;
        return StringUtils.ContainsIgnoreCase(value, nullRef);
    }

    /// <summary>
    /// Parses out a string that may contain links to context variables
    /// </summary>
    /// <param name="environment"></param>
    /// <param name="ctx">The context to use for logging, if not provided failures are not logged</param>
    /// <param name="value"></param>
    /// <param name="targetType"></param>
    /// <returns></returns>
    public static object GetStringDefinedObject(Hashtable environment, INamingContext ctx, string value, Type targetType)
    {
      if(HasNullReference(value))
      {
        return null;
      }
      if (StringUtils.EqualsIgnoreCase(value, ESCAPE_LINK_START + ESCAPE_VALUE_LOGGER + ESCAPE_LINK_END) && ctx is InitialContext)
      {
        return ((InitialContext)ctx).Logger;
      }
      if (StringUtils.EqualsIgnoreCase(value, ESCAPE_LINK_START + ESCAPE_VALUE_CONTEXT + ESCAPE_LINK_END) && ctx is InitialContext)
      {
        return ctx;
      }
      while (StringUtils.Contains(value, ESCAPE_LINK_START))
      {
        string rootRef = ESCAPE_LINK_START + ESCAPE_VALUE_ROOT + ESCAPE_LINK_END;
        if (StringUtils.ContainsIgnoreCase(value, rootRef))
        {
          string rootValue = environment[NamingContextConstants.LOCAL_CONTEXT_ROOT] != null ? (string)environment[NamingContextConstants.LOCAL_CONTEXT_ROOT] : "";
          value = StringUtils.ReplaceIgnoreCase(value, rootRef, rootValue);
        }
        else if(environment[NamingContextConstants.LOCAL_GLOBAL_ENTRIES] != null)
        {
          int strt = value.IndexOf(ESCAPE_LINK_START) + ESCAPE_LINK_START.Length;
          int lnght = value.Length - strt - (value.Length - value.IndexOf(ESCAPE_LINK_END));
          string refName = value.Substring(strt, lnght);
          string refValue = "";
          int firstIndexOfDot = refName.IndexOf(".");
          int firstIndexOfSquareBracket = refName.IndexOf("[");
          if (StringUtils.Contains(refName,".") && 
                (firstIndexOfSquareBracket < 0 || firstIndexOfDot < firstIndexOfSquareBracket ) )
          {
            StringTokenizer tokenizer = new StringTokenizer(refName, ".");
            string envName = tokenizer.NextToken();
            object obj = null;
            if (StringUtils.NotEmpty(envName))
            {
              obj = ((IDictionary)environment[NamingContextConstants.LOCAL_GLOBAL_ENTRIES])[envName] != null ? ((IDictionary)environment[NamingContextConstants.LOCAL_GLOBAL_ENTRIES])[envName] : null;
              if (obj != null)
              {
                while (tokenizer.HasMoreTokens())
                {
                  if (obj == null)
                  {
                    break;
                  }
                  string token = tokenizer.NextToken();
                  string propName = token;
                  if (StringUtils.NotEmpty(token))
                  {
                    bool hasIndex = false;
                    if (StringUtils.Contains(token,"["))
                    {
                      hasIndex = true;
                      propName = token.Substring(0, token.IndexOf("["));
                    }
                    PropertyInfo prop = obj.GetType().GetProperty(propName);
                    if (prop != null && prop.CanRead)
                    {
                      if (prop.PropertyType is IDictionary)
                      {
                        IDictionary dictObj = (IDictionary)prop.GetValue(obj, null);
                        if (hasIndex)
                        {
                          obj = dictObj[StringUtils.ExtractFromDelimiters(token, "[", "]")];
                        }
                        else
                        {
                          obj = dictObj;
                        }
                      }
                      else if (prop.PropertyType is IList)
                      {
                        IList listObj = (IList)prop.GetValue(obj, null);
                        if (hasIndex)
                        {
                          try
                          {
                            int indx = Convert.ToInt32(StringUtils.ExtractFromDelimiters(token, "[", "]"));
                            obj = listObj[indx];
                          }
                          catch
                          {
                            obj = listObj;
                          }
                        }
                        else
                        {
                          obj = listObj;
                        }
                      }
                      else
                      {
                        obj = prop.GetValue(obj, null);
                      }
                    }
                    else
                    {
                      if (ctx != null)
                      {
                        ctx.Log("ContextValueParser:GetStringDefinedObject: Property " + propName + " not found or has no read access in object of type " + obj.GetType().FullName, null, LogLevel.Error);
                      }
                      obj = null;
                      break;
                    }
                  }
                }
              }
            }
            refValue = obj != null ? obj.ToString() : "";
          }
          else
          {
            bool hasIndex = false;
            string propName = refName;
            if (StringUtils.Contains(refName,"["))
            {
              hasIndex = true;
              propName = refName.Substring(0, refName.IndexOf("["));
            }
            object obj = ((IDictionary)environment[NamingContextConstants.LOCAL_GLOBAL_ENTRIES])[propName];
            if(obj == null)
            {
              refValue = "";
            }
            else
            {
              if(obj is IDictionary)
              {
                if (hasIndex)
                {
                  IDictionary dictObj = (IDictionary)obj;
                  obj = dictObj[StringUtils.ExtractFromDelimiters(refName, "[", "]")];
                }
              }
              else if(obj is IList)
              {
                if (hasIndex)
                {
                  try
                  {
                    IList listObj = (IList)obj;
                    int indx = Convert.ToInt32(StringUtils.ExtractFromDelimiters(refName, "[", "]"));
                    obj = listObj[indx];
                  }
                  catch(Exception e)
                  {
                    throw new ResourceInitializationException("Could not get object from list", e);
                  }
                }
              }
              refValue = obj.ToString();
            }
          }
          value = StringUtils.ReplaceIgnoreCase(value, ESCAPE_LINK_START + refName + ESCAPE_LINK_END, refValue);
        }
      }

      try
      {
        return Convert.ChangeType(value, targetType);
      }
      catch (Exception e)
      {
        ctx.Log("Could not cast from environment the value " + value + " to argument type " + targetType.ToString(), e, LogLevel.Error);
        return null;
      }
    }
  }
}
